"""
Request models for CodeMCP API.

Defines Pydantic models for incoming API requests.
"""

from typing import Dict, Any, List, Optional, Union
from pydantic import BaseModel, Field, validator
from enum import Enum


class AnalysisMode(str, Enum):
    """Analysis mode options."""
    AUTOMATIC = "automatic"
    MANUAL = "manual"


class ExportFormat(str, Enum):
    """Export format options."""
    JSON = "json"
    HTML = "html"
    DOT = "dot"
    MARKDOWN = "markdown"


class LanguageType(str, Enum):
    """Supported language types."""
    PYTHON = "python"
    CPP = "cpp"
    FORTRAN = "fortran"
    TYPESCRIPT = "typescript"
    LATEX = "latex"
    MARKDOWN = "markdown"


class AnalysisRequest(BaseModel):
    """Request model for code analysis."""
    file_path: str = Field(..., description="Path to the file to analyze")
    function_name: Optional[str] = Field(None, description="Specific function to analyze")
    language: Optional[LanguageType] = Field(None, description="Language type override")
    mode: AnalysisMode = Field(AnalysisMode.AUTOMATIC, description="Analysis mode")
    max_depth: int = Field(10, ge=1, le=100, description="Maximum analysis depth")
    include_dependencies: bool = Field(False, description="Include dependency analysis")
    cross_language: bool = Field(False, description="Enable cross-language analysis")
    cache_results: bool = Field(True, description="Cache analysis results")
    session_id: Optional[str] = Field(None, description="Session identifier")
    
    @validator('file_path')
    def validate_file_path(cls, v):
        if not v or not v.strip():
            raise ValueError("File path cannot be empty")
        return v.strip()
    
    @validator('function_name')
    def validate_function_name(cls, v):
        if v is not None and not v.strip():
            raise ValueError("Function name cannot be empty string")
        return v.strip() if v else None


class ExplorationRequest(BaseModel):
    """Request model for interactive exploration."""
    session_id: str = Field(..., description="Session identifier")
    action: str = Field(..., description="Exploration action")
    parameters: Dict[str, Any] = Field(default_factory=dict, description="Action parameters")
    current_node_id: Optional[str] = Field(None, description="Current node identifier")
    
    class Config:
        schema_extra = {
            "example": {
                "session_id": "session_123",
                "action": "select_nodes",
                "parameters": {
                    "selections": [{"node_id": "node_1", "weight": 0.7}]
                },
                "current_node_id": "root_node"
            }
        }


class FunctionInfoRequest(BaseModel):
    """Request model for function information."""
    file_path: str = Field(..., description="Path to the file")
    function_name: str = Field(..., description="Function name")
    language: Optional[LanguageType] = Field(None, description="Language type override")
    include_callers: bool = Field(False, description="Include caller information")
    include_callees: bool = Field(False, description="Include callee information")
    include_source: bool = Field(False, description="Include source code")
    
    @validator('file_path', 'function_name')
    def validate_non_empty(cls, v):
        if not v or not v.strip():
            raise ValueError("Value cannot be empty")
        return v.strip()


class CallChainRequest(BaseModel):
    """Request model for call chain analysis."""
    start_function: str = Field(..., description="Starting function name")
    end_function: str = Field(..., description="Target function name")
    file_path: Optional[str] = Field(None, description="File path for analysis scope")
    language: Optional[LanguageType] = Field(None, description="Language type")
    max_depth: int = Field(20, ge=1, le=200, description="Maximum search depth")
    bidirectional: bool = Field(False, description="Search in both directions")
    
    @validator('start_function', 'end_function')
    def validate_function_names(cls, v):
        if not v or not v.strip():
            raise ValueError("Function name cannot be empty")
        return v.strip()


class BatchAnalysisRequest(BaseModel):
    """Request model for batch analysis of multiple files."""
    file_paths: List[str] = Field(..., min_items=1, description="List of file paths")
    language: Optional[LanguageType] = Field(None, description="Language type override")
    entry_points: Optional[List[str]] = Field(None, description="Entry point functions")
    max_depth: int = Field(10, ge=1, le=100, description="Maximum analysis depth")
    parallel_processing: bool = Field(True, description="Enable parallel processing")
    export_format: Optional[ExportFormat] = Field(None, description="Export format")
    
    @validator('file_paths')
    def validate_file_paths(cls, v):
        if not v:
            raise ValueError("At least one file path is required")
        
        for path in v:
            if not path or not path.strip():
                raise ValueError("File paths cannot be empty")
        
        return [path.strip() for path in v]


class NodeSelectionRequest(BaseModel):
    """Request model for node selection in manual mode."""
    session_id: str = Field(..., description="Session identifier")
    current_node_id: str = Field(..., description="Current node identifier")
    selections: List[Dict[str, Union[str, float]]] = Field(
        ..., 
        description="List of selected nodes with weights"
    )
    
    class Config:
        schema_extra = {
            "example": {
                "session_id": "session_123",
                "current_node_id": "node_abc",
                "selections": [
                    {"node_id": "child_1", "weight": 0.6},
                    {"node_id": "child_2", "weight": 0.4}
                ]
            }
        }


class CodeViewRequest(BaseModel):
    """Request model for viewing code of a specific node."""
    session_id: str = Field(..., description="Session identifier")
    node_id: str = Field(..., description="Node identifier")
    include_context: bool = Field(False, description="Include surrounding context")
    context_lines: int = Field(5, ge=0, le=50, description="Number of context lines")
    syntax_highlighting: bool = Field(True, description="Apply syntax highlighting")


class ExportRequest(BaseModel):
    """Request model for exporting analysis results."""
    session_id: str = Field(..., description="Session identifier")
    format: ExportFormat = Field(..., description="Export format")
    include_metadata: bool = Field(True, description="Include metadata")
    include_statistics: bool = Field(True, description="Include statistics")
    output_path: Optional[str] = Field(None, description="Output file path")
    
    class Config:
        schema_extra = {
            "example": {
                "session_id": "session_123",
                "format": "json",
                "include_metadata": True,
                "include_statistics": True,
                "output_path": "/tmp/analysis_results.json"
            }
        }


class SessionCreateRequest(BaseModel):
    """Request model for creating a new session."""
    session_type: str = Field("analysis", description="Type of session")
    configuration: Dict[str, Any] = Field(default_factory=dict, description="Session configuration")
    timeout_minutes: int = Field(60, ge=1, le=1440, description="Session timeout in minutes")
    
    class Config:
        schema_extra = {
            "example": {
                "session_type": "manual_exploration",
                "configuration": {
                    "mode": "manual",
                    "syntax_highlighting": True,
                    "max_depth": 20
                },
                "timeout_minutes": 120
            }
        }


class SessionUpdateRequest(BaseModel):
    """Request model for updating session configuration."""
    session_id: str = Field(..., description="Session identifier")
    configuration: Dict[str, Any] = Field(..., description="Updated configuration")
    extend_timeout: Optional[int] = Field(None, description="Extend timeout by minutes")


class DependencyAnalysisRequest(BaseModel):
    """Request model for dependency analysis."""
    file_path: str = Field(..., description="Path to the file")
    language: Optional[LanguageType] = Field(None, description="Language type override")
    include_external: bool = Field(True, description="Include external dependencies")
    include_internal: bool = Field(True, description="Include internal dependencies")
    depth: int = Field(1, ge=1, le=10, description="Dependency depth")
    
    @validator('file_path')
    def validate_file_path(cls, v):
        if not v or not v.strip():
            raise ValueError("File path cannot be empty")
        return v.strip()


class ValidateFileRequest(BaseModel):
    """Request model for file validation."""
    file_path: str = Field(..., description="Path to the file to validate")
    language: Optional[LanguageType] = Field(None, description="Expected language type")
    check_syntax: bool = Field(True, description="Perform syntax validation")
    
    @validator('file_path')
    def validate_file_path(cls, v):
        if not v or not v.strip():
            raise ValueError("File path cannot be empty")
        return v.strip()


class SearchRequest(BaseModel):
    """Request model for searching within analysis results."""
    session_id: Optional[str] = Field(None, description="Session identifier for scoped search")
    query: str = Field(..., description="Search query")
    search_type: str = Field("function", description="Type of search (function, file, pattern)")
    case_sensitive: bool = Field(False, description="Case sensitive search")
    regex: bool = Field(False, description="Use regular expressions")
    max_results: int = Field(100, ge=1, le=1000, description="Maximum number of results")
    
    @validator('query')
    def validate_query(cls, v):
        if not v or not v.strip():
            raise ValueError("Search query cannot be empty")
        return v.strip()


class ToolListRequest(BaseModel):
    """Request model for listing available tools."""
    language: Optional[LanguageType] = Field(None, description="Filter by language")
    tool_type: Optional[str] = Field(None, description="Filter by tool type")
    include_disabled: bool = Field(False, description="Include disabled tools")